﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using SimpleIR;

namespace InterpreterCore
{
	/// <summary>
	/// Methods that drive the execution of the program.
	/// </summary>
	internal static class LocalExecution
	{
		private static bool PreBreakTest(int line, DebugState dbg)
		{ return (dbg >= DebugState.DebugStep) || ((dbg == DebugState.DebugBreakAt) && line == GlobalInterpreterState.BreakStatementID); }

		private static void ProcessDebugCommandsStmt(IRByteCode bc)
		{
			String cmd;
			String sstr = bc.ToString();

			while(true)
			{
				Console.WriteLine(String.Format("Current Method: {0}", GlobalInterpreterState.ActiveCallFrame.MethodIdentity.ToString()));
				Console.Write("\nstmt: " + bc.UseModLoc + ", " + sstr + "\n" + "> ");
				cmd = Console.ReadLine();

				if(cmd.Equals("r") || cmd.Equals("run"))
				{
					GlobalInterpreterState.ActiveCallFrame.DebugStatus = DebugState.DebugBreakAt;
					break;
				}
				else if(cmd.StartsWith("b ") || cmd.StartsWith("break "))
				{
					int bl = cmd.IndexOf(' ');
					String target = cmd.Substring(bl + 1);
					GlobalInterpreterState.BreakStatementID = Int32.Parse(target);
				}
				else if(cmd.Equals("d") || cmd.Equals("display"))
				{
					string ds = GlobalInterpreterState.ActiveCallFrame.DebuggerString();
					Console.WriteLine("Current Local State:");
					Console.WriteLine(ds);
				}
				else if(cmd.Equals("dh") || cmd.Equals("displayheap"))
				{
					Console.WriteLine("Doing Heap Dump to DGML");

					string chfile = System.IO.Path.Combine(GlobalInterpreterState.DumpDir, "concreteheap.dgml");
					EmitConcreteDGMLToFile(chfile);
				}
				else if(cmd.Equals("si") || cmd.Equals("stepinto"))
				{
					GlobalInterpreterState.ActiveCallFrame.DebugStatus = DebugState.DebugStepInto;
					break;
				}
				else if(cmd.Equals("s") || cmd.Equals("step"))
				{
					GlobalInterpreterState.ActiveCallFrame.DebugStatus = DebugState.DebugStep;
					break;
				}
				else if(cmd.Equals("p") || cmd.Equals("print"))
				{
					IRBodyDef tbdf = GlobalInterpreterState.TProg.MethodBodyTable.GetBodyForInvokeExactSigMatch(GlobalInterpreterState.ActiveCallFrame.MethodIdentity);
					string fgfile = System.IO.Path.Combine(GlobalInterpreterState.DumpDir, "flowgraph.dgml");
					string fgdgml = tbdf.Body.DGMLDisplayString();

					System.IO.File.WriteAllText(fgfile, fgdgml);
					System.Diagnostics.Process.Start(fgfile);
				}
				else if(cmd.Equals("cg") || cmd.Equals("callgraph"))
				{
					System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, CloseOutput = true, Encoding = Encoding.UTF8 };

					String ffn = System.IO.Path.Combine(GlobalInterpreterState.DumpDir, "partialcg.dgml");
					using(System.Xml.XmlWriter cgwriter = System.Xml.XmlTextWriter.Create(ffn, settings))
					{
						GlobalInterpreterState.TProg.CallGraph.SerializeToDGML(cgwriter, GlobalInterpreterState.TProg);
						System.Diagnostics.Process.Start(ffn);
					}
				}
				else
				{
					Console.WriteLine("Unknown Command");
					Console.WriteLine("run [r]: Analyze till breakpoint.");
					Console.WriteLine("break [b]: Set a breakpoint at the line/function.");
					Console.WriteLine("display [d]: Display the current analysis state.");
					Console.WriteLine("stepinto [si]: Analyze the stmt, going into call if needed.");
					Console.WriteLine("step [s]: Analyze the stmt, do not go into expressions.");
					Console.WriteLine("print [p]: Display flow graph for the current method.");
					Console.WriteLine("callgraph [cg]: Display the call graph for the program.");
				}
			}
		}


		private static string s_dgmlh = "<?xml version='1.0' encoding='utf-8'?>\n<DirectedGraph  xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\">";
		private static string s_dgmlt = "</DirectedGraph>";

		private static void EmitConcreteDGMLToFile(string file)
		{
			System.Xml.XmlWriterSettings ws = new System.Xml.XmlWriterSettings() { CloseOutput = true, Indent = true, ConformanceLevel = System.Xml.ConformanceLevel.Fragment };
			
			StringBuilder sbn = new StringBuilder();
			System.Xml.XmlWriter xwn = System.Xml.XmlWriter.Create(sbn, ws);
			xwn.WriteStartElement("Nodes");

			StringBuilder sbe = new StringBuilder();
			System.Xml.XmlWriter xwe = System.Xml.XmlWriter.Create(sbe, ws);
			xwe.WriteStartElement("Links");

			HashSet<int> reachaddrs = new HashSet<int>();
			GlobalInterpreterState.WalkAllReachableFromRoots(reachaddrs);
			GlobalInterpreterState.ActiveCallFrame.WalkAllReachableFromRoots(reachaddrs);
			GlobalInterpreterState.SerializeForDGMLDisplay(xwn, xwe, true);

			MemoryState.SerializeForDGMLDisplay(xwn, xwe, reachaddrs);
			xwn.WriteEndElement();
			xwe.WriteEndElement();
			xwn.Close();
			xwe.Close();

			string oos = String.Format("{0}\n{1}\n{2}\n{3}\n", s_dgmlh, sbn.ToString(), sbe.ToString(), s_dgmlt);

			System.IO.File.WriteAllText(file, oos);
		}

		//////////////////////////////////////////////////////////////
		//Analyze Statements 

		//////////////
		//Execution of control flow structures

		/// <summary>
		/// Execute a single flow block
		/// </summary>
		internal static void SimulateBody()
		{
			long cblockid = GlobalInterpreterState.ActiveCallFrame.CurrentBlock;
			
			while(cblockid != -1)
			{
				IRBasicBlock cblock = GlobalInterpreterState.ActiveCallFrame.MethodBody.Body.GetBlockWithID(cblockid);
				SimulateBasicBlock(cblock);

				cblockid = GlobalInterpreterState.ActiveCallFrame.CurrentBlock;
			}
		}

		/// <summary>
		/// Execute a single basic block
		/// </summary>
		private static void SimulateBasicBlock(IRBasicBlock bb)
		{
			GlobalInterpreterState.ActiveCallFrame.ClearVarsNotInScopeAddNew(bb.InScopeAtEntry, bb.LocalsToTypeMap);

			for(int i = 0; i < bb.Operations.Count; ++i)
			{
				IRByteCode bc = bb.Operations[i];

				SimulateStmt(bc);

				/*
				try
				{
					SimulateStmt(bc);
				}
				catch(Exception e)
				{
					Console.WriteLine(e.ToString());
					Console.WriteLine("Starting Debugger");

					LocalCallFrame ccf = GlobalInterpreterState.ActiveCallFrame;
					ccf.DebugStatus = DebugState.DebugStep;
					SimulateStmt(bc);
				}
				*/ 
			}
		}

		/// <summary>
		/// Execute a single stmt
		/// </summary>
		private static void SimulateStmt(IRByteCode bc)
		{
			LocalCallFrame ccf = GlobalInterpreterState.ActiveCallFrame;
			GlobalInterpreterState.SetCurrentLine(bc.UseModLoc, bc.SourceLocationInfo);
			GlobalInterpreterState.IncrementTime();

			if(PreBreakTest(bc.UseModLoc, ccf.DebugStatus))
			{
				Console.WriteLine("\nBefore Stmt Analysis");
				ProcessDebugCommandsStmt(bc);
			}

			ccf.EmitEvalStackForTraceAtStmtExecute();

			switch(bc.OperationCode)
			{
				case OpCode.Op.BranchU:
					ccf.SetPCToBlockStart(((IRBranchOp)bc).BRUTarget);
					break;
				case OpCode.Op.BranchC:
					AnalyzeBranchCond((IRBranchOp)bc, ccf);
					break;
				case OpCode.Op.Switch:
					AnalyzeSwitch((IRBranchOp)bc, ccf);
					break;
				case OpCode.Op.Throw:
					throw new InterpRuntimeException("User Throw: Currently Force Failure!");
				case OpCode.Op.Return:
					AnalyzeReturn((IRBranchOp)bc, ccf);
					break;
				case OpCode.Op.Call:
					if(((IRCallOp)bc).InvokeSig is IRVirtualInvoke)
						AnalyzeVirtualCall((IRCallOp)bc);
					else
						AnalyzeStaticCall((IRCallOp)bc);
					break;
				case OpCode.Op.CallVirt:
					IRCallOp invbc = ((IRCallOp)bc);
					if(invbc.ReciverExpType is IRPtrType && ((IRPtrType)invbc.ReciverExpType).PtrTargetType is IRLambdaType && invbc.InvokeSig.InvokeName.Equals("Invoke"))
						AnalyzeLambdaInvoke((IRCallOp)bc);
					else
						AnalyzeVirtualCall((IRCallOp)bc);
					break;
				case OpCode.Op.AllocCons:
					AnalyzeAllocObj((IRAllocationOp)bc);
					break;
				case OpCode.Op.PrimEQ:
					ccf.SimulatePrimEQ();
					break;
				case OpCode.Op.Less:
					ccf.SimulateLess();
					break;
				case OpCode.Op.LessU:
					ccf.SimulateLessUnsigned();
					break;
				case OpCode.Op.LessEQ:
					ccf.SimulateLessEQ();
					break;
				case OpCode.Op.LessEQU:
					ccf.SimulateLessEQUnsigned();
					break;
				case OpCode.Op.Greater:
					ccf.SimulateGreater();
					break;
				case OpCode.Op.GreaterU:
					ccf.SimulateGreaterUnsigned();
					break;
				case OpCode.Op.GreaterEQ:
					ccf.SimulateGreaterEQ();
					break;
				case OpCode.Op.GreaterEQU:
					ccf.SimulateGreaterEQUnsigned();
					break;
				case OpCode.Op.Add:
					ccf.SimulateAdd();
					break;
				case OpCode.Op.AddU:
					ccf.SimulateAddUnsigned();
					break;
				case OpCode.Op.Div:
					ccf.SimulateDiv();
					break;
				case OpCode.Op.DivU:
					ccf.SimulateDivUnsigned();
					break;
				case OpCode.Op.Mult:
					ccf.SimulateMult();
					break;
				case OpCode.Op.MultU:
					ccf.SimulateMultUnsigned();
					break;
				case OpCode.Op.Neg:
					ccf.SimulateNeg();
					break;
				case OpCode.Op.Rem:
					ccf.SimulateRem();
					break;
				case OpCode.Op.RemU:
					ccf.SimulateRemUnsigned();
					break;
				case OpCode.Op.Sub:
					ccf.SimulateSub();
					break;
				case OpCode.Op.SubU:
					ccf.SimulateSubUnsigned();
					break;
				case OpCode.Op.And:
					ccf.SimulateAnd();
					break;
				case OpCode.Op.Or:
					ccf.SimulateOr();
					break;
				case OpCode.Op.Not:
					ccf.SimulateNot();
					break;
				case OpCode.Op.Shl:
					ccf.SimulateShl();
					break;
				case OpCode.Op.Shr:
					ccf.SimulateShr();
					break;
				case OpCode.Op.ShrU:
					ccf.SimulateShrUnsigned();
					break;
				case OpCode.Op.Xor:
					ccf.SimulateXor();
					break;
				case OpCode.Op.Conv:
					ccf.SimulateConv((IRPrimitiveNumericType)((IREvalStackTypeOp)bc).TypeArg);
					break;
				case OpCode.Op.ConvU:
					ccf.SimulateConvUnsigned((IRPrimitiveNumericType)((IREvalStackTypeOp)bc).TypeArg);
					break;
				case OpCode.Op.LoadFromLocal:
					ccf.SimulateLoadFromLocal(((IRLoadToEvalStackBasic)bc).LocalVar);
					break;
				case OpCode.Op.StoreToLocal:
					ccf.SimulateStoreToLocal(((IRStoreFromEvalStackBasic)bc).LocalVar);
					break;
				case OpCode.Op.LoadFromGlobal:
					ccf.SimulateLoadFromGlobal(((IRLoadToEvalStackBasic)bc).StaticField, GlobalInterpreterState.GlobalFieldsMap);
					break;
				case OpCode.Op.StoreToGlobal:
					ccf.SimulateStoreToGlobal(((IRStoreFromEvalStackBasic)bc).StaticField, GlobalInterpreterState.GlobalFieldsMap);
					break;
				case OpCode.Op.ArrayLoad:
					ccf.SimulateArrayRead(((IRLoadToEvalStackIndirect)bc).ArrayLoadType.ArrayContentsType);
					break;
				case OpCode.Op.ArrayStore:
					ccf.SimulateArrayStore(((IRStoreFromEvalStackIndirect)bc).ArrayStoreType.ArrayContentsType);
					break;
				case OpCode.Op.ArrayLength:
					ccf.SimulateArrayLength();
					break;
				case OpCode.Op.IndirectRead:
					ccf.SimulateDeRefIndirectRead();
					break;
				case OpCode.Op.IndirectWrite:
					ccf.SimulateDeRefIndirectWrite();
					break;
				case OpCode.Op.LoadMemberField:
					ccf.SimulateObjectFieldLoad(((IRLoadToEvalStackIndirect)bc).LoadField);
					break;
				case OpCode.Op.StoreMemberField:
					ccf.SimulateObjectFieldWrite(((IRStoreFromEvalStackIndirect)bc).WriteField);
					break;
				case OpCode.Op.IndirectInitialize:
					ccf.SimulateDeRefIndirectInitialize(((IRStoreFromEvalStackIndirect)bc).ThroughRefType.ReferenceTargetType);
					break;
				case OpCode.Op.LoadStructField:
					ccf.SimulateReadFromStructOnStack(((IRLoadToEvalStackIndirect)bc).LoadField);
					break;
				case OpCode.Op.LoadLocalAddr:
					ccf.SimulateLoadLocalAddr(((IRPointerIndexOp)bc).LocalName);
					break;
				case OpCode.Op.LoadGlobalAddr:
					ccf.SimulateLoadGlobalAddr(((IRLoadToEvalStackBasic)bc).StaticField, GlobalInterpreterState.GlobalFieldsMap);
					break;
				case OpCode.Op.IndexRefAddrByField:
					ccf.SimulateIndexAddrByField(((IRPointerIndexOp)bc).IndexField, ((IRPointerIndexOp)bc).IndexStackOffset);
					break;
				case OpCode.Op.IndexRefAddrByArrayIndex:
					ccf.SimulateIndexAddrInsideArray(((IRPointerIndexOp)bc).ArrayType.ContentsField);
					break;
				case OpCode.Op.LoadFuncPtr:
					ccf.SimulateLoadFuncPtr(((IRLoadToEvalStackBasic)bc).InvokeIdentity);
					break;
				case OpCode.Op.LoadVFuncPtr:
					Debug.Assert(false, "Load VFunc pointer Not Supported Yet.");
					break;
				case OpCode.Op.DerefableEQ:
					ccf.SimulateDrefableEQ();
					break;
				case OpCode.Op.NonNull:
					ccf.SimulatePointerNonNull();
					break;
				case OpCode.Op.Box:
					ccf.SimulateBox((IRValueType)((IREvalStackTypeOp)bc).TypeArg);
					break;
				case OpCode.Op.UnBox:
					ccf.SimulateUnBox((IRValueType)((IREvalStackTypeOp)bc).TypeArg, 0);
					break;
				case OpCode.Op.Cast:
					ccf.SimulateCast((IRPtrType)((IREvalStackTypeOp)bc).TypeArg);
					break;
				case OpCode.Op.IsInst:
					ccf.SimulateIsInst((IRPtrType)((IREvalStackTypeOp)bc).TypeArg);
					break;
				case OpCode.Op.Constrain:
					ccf.SimulateConstrain(((IREvalStackTypeOp)bc).TypeArg, ((IREvalStackTypeOp)bc).ConstrainPos);
					break;
				case OpCode.Op.Pop:
					ccf.SimulatePop();
					break;
				case OpCode.Op.Dup:
					ccf.SimulateDup();
					break;
				case OpCode.Op.PushNull:
					ccf.SimulatePushNull();
					break;
				case OpCode.Op.PushInt:
				case OpCode.Op.PushFloat:
					SimulateLoadConstToStack((IRLoadToEvalStackBasic)bc, ccf);
					break;
				case OpCode.Op.PushString:
					ccf.SimulatePushString(((IRLoadToEvalStackBasic)bc).Literal.StringVal);
					break;
				default:
					Debug.Assert(false, "Operation Not Defined!");
					break;
			}
		}

		private static void AnalyzeBranchCond(IRBranchOp bc, LocalCallFrame ccf)
		{
			bool bval = ccf.StackPopBoolBranchValue();
			if(bval)
			{
				TracingSystem.LogJump(ccf.CurrentBlock, bc.BRCondTrueTarget);
				ccf.SetPCToBlockStart(bc.BRCondTrueTarget);
			}
			else
			{
				TracingSystem.LogJump(ccf.CurrentBlock, bc.BRCondFalseTarget);
				ccf.SetPCToBlockStart(bc.BRCondFalseTarget);
			}
		}

		private static void AnalyzeSwitch(IRBranchOp bc, LocalCallFrame ccf)
		{
			int sval = ccf.StackPopSwitchValue();
			long bbid = bc.SwitchTargets[sval];

			TracingSystem.LogJump(ccf.CurrentBlock, bbid);
			ccf.SetPCToBlockStart(bbid);
		}

		private static void SimulateLoadConstToStack(IRLoadToEvalStackBasic bc, LocalCallFrame ccf)
		{
			IRLiteralExp litval = bc.Literal;
			
			IRPrimitiveNumericType pv = (IRPrimitiveNumericType)litval.LiteralType;
			if(pv is IRSignedIntegerType)
				ccf.SimulatePushInt((IRSignedIntegerType)pv, litval.IntVal);
			else
				ccf.SimulatePushFloat(litval.FloatVal);
		}

		private static void AnalyzeReturn(IRBranchOp bc, LocalCallFrame ccf)
		{
			ccf.SetPCToBlockStart(-1);
		}

		private static void AnalyzeVirtualCall(IRCallOp bc)
		{
			Debug.Assert(bc.InvokeSig is IRVirtualInvoke);

			LocalCallFrame ccf = GlobalInterpreterState.ActiveCallFrame;
			int rcvrpos = bc.InvokeSig.InvokeArgs.Count + 1;

			if(!ccf.IsVCallNonNull(rcvrpos - 1))
				throw new InterpRuntimeException("Null Exception on Invoke!");

			List<IRType> argtl = bc.InvokeSig.InvokeArgs;
			IRType stckrcvr = ccf.GetTargetEvalStackRcvrType(rcvrpos - 1);
			
			Tuple<IRType, IRInvokeSig> nettypeandsig = new Tuple<IRType, IRInvokeSig>(bc.BaseImplementationType, bc.InvokeSig);
			IRBodyDef bd;
			if(stckrcvr is IRReferenceType)
				bd = GlobalInterpreterState.TProg.GetBodyForClassAndSig(((IRReferenceType)stckrcvr).ReferenceTargetType, nettypeandsig);
			else
			{
				IRClassType actualimpltype = ccf.GetTargetCallClassForName(rcvrpos - 1);
				
				IRType actualImplClass;
				if(actualimpltype is IRBoxedType)
				{
					IRValueType btv = ((IRBoxedType)actualimpltype).BoxedTypeValue;
					IRType baselookuptype = (bc.HasSpecificTarget ? bc.SpecificTargetClass : btv);
					actualImplClass = IRProgram.GetImplClassWithThisAsBaseType(baselookuptype, nettypeandsig);

					ccf.SimulateUnBox(btv, rcvrpos - 1);
				}
				else
				{
					ccf.SetArgType(GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor((IRClassType)actualimpltype), rcvrpos);
					IRType baselookuptype = (bc.HasSpecificTarget ? bc.SpecificTargetClass : actualimpltype);
					actualImplClass = IRProgram.GetImplClassWithThisAsBaseType(baselookuptype, nettypeandsig);
				}

				bd = GlobalInterpreterState.TProg.GetBodyForClassAndSig(actualImplClass, nettypeandsig);
			}

			for(int j = 0; j < argtl.Count; ++j)
			{
				IRType tt = argtl[j];
				ccf.SetArgType(tt, rcvrpos - (j + 1));
			}

			DoAnalyzeOfCall(bc, bd, false);
		}

		private static void AnalyzeLambdaInvoke(IRCallOp bc)
		{
			Debug.Assert(bc.InvokeSig is IRVirtualInvoke);

			LocalCallFrame ccf = GlobalInterpreterState.ActiveCallFrame;
			List<IRType> argtl = bc.InvokeSig.InvokeArgs;
			int stackstart = bc.InvokeSig.InvokeArgs.Count + 1;

			IRInvokeIdentity iid = ccf.GetLambdaInvokeTarget(stackstart);
			Tuple<IRType, IRInvokeSig> nettypeandsig = new Tuple<IRType, IRInvokeSig>(iid.DefInClass, iid.InvSig);
			IRBodyDef bd = GlobalInterpreterState.TProg.GetBodyForClassAndSig(iid.DefInClass, nettypeandsig);

			for(int j = 0; j < argtl.Count; ++j)
			{
				IRType tt = argtl[j];
				ccf.SetArgType(tt, stackstart - (j + 1));
			}

			ccf.PopOrConvertLambdaAsNeeded(stackstart, iid);

			DoAnalyzeOfCall(bc, bd, false);
		}

		private static void AnalyzeStaticCall(IRCallOp bc)
		{
			Debug.Assert(bc.InvokeSig is IRStaticInvoke);

			LocalCallFrame ccf = GlobalInterpreterState.ActiveCallFrame;
			Tuple<IRType, IRInvokeSig> nettypeandsig = new Tuple<IRType, IRInvokeSig>(bc.SpecificTargetClass, bc.InvokeSig);
			IRBodyDef bd = GlobalInterpreterState.TProg.GetBodyForClassAndSig(bc.SpecificTargetClass, nettypeandsig);
			List<IRType> argtl = bc.InvokeSig.InvokeArgs;
			int stackstart = argtl.Count;

			for(int j = 0; j < argtl.Count; ++j)
			{
				IRType tt = argtl[j];
				ccf.SetArgType(tt, stackstart - j);
			}

			DoAnalyzeOfCall(bc, bd, false);
		}

		private static void AnalyzeAllocObj(IRAllocationOp bc)
		{
			LocalCallFrame ccf = GlobalInterpreterState.ActiveCallFrame;

			if(bc.AllocType is IRLambdaType)
				ccf.SimulateAllocDelegate((IRLambdaType)bc.AllocType);
			else if(bc.IsArrayAlloc)
				ccf.SimulateAllocArray((IRArrayType)bc.AllocType);
			else
			{
				Tuple<IRType, IRInvokeSig> nettypeandsig = new Tuple<IRType, IRInvokeSig>(bc.AllocType, bc.ConsIdentity.InvSig);
				IRBodyDef bd = GlobalInterpreterState.TProg.GetBodyForClassAndSig(bc.AllocType, nettypeandsig);
				List<IRType> argtl = bc.ConsIdentity.InvSig.InvokeArgs;

				ccf.SimulateAllocBlank(bc.AllocType);
				if(bc.AllocType is IRStructType)
				{
					ccf.SimulateBox((IRValueType)bc.AllocType);
					ccf.SimulateUnBox((IRValueType)bc.AllocType, 0);
				}

				int stackstart = argtl.Count + 1;
				ccf.RotateBlankAllocToRecvrPosition(stackstart);

				for(int j = 0; j < argtl.Count; ++j)
				{
					IRType tt = argtl[j];
					ccf.SetArgType(tt, stackstart - (j + 1));
				}

				DoAnalyzeOfCall(bc, bd, true);
			}
		}

		//////////////////////////////////////////////////////////////
		//Support for Inter-procedural Analysis

		/// <summary>
		/// Move from the caller scope into the callee scope, setting the parameter names and as needed unboxing the reciver argument
		/// <c>rcvrtype</c> is ignored if the method is actually static for static methods.
		/// </summary>
		private static void MoveIntoCalleeScope(IRBodyDef calledBody, int paramcount)
		{
			LocalCallFrame ccf = GlobalInterpreterState.ActiveCallFrame;
			DebugState ndbg;
			if(ccf.DebugStatus == DebugState.DebugBreakAt || ccf.DebugStatus == DebugState.DebugRun)
				ndbg = ccf.DebugStatus;
			else if(ccf.DebugStatus == DebugState.DebugStepInto)
				ndbg = DebugState.DebugStep;
			else
				ndbg = DebugState.DebugRun;

			LocalCallFrame ncf = new LocalCallFrame(ndbg, calledBody);
			ncf.MoveCallArgsToThis(ccf, paramcount);

			for(int i = calledBody.Parameters.Count - 1; i >= 0; --i)
				ncf.PopStackArgIntoLocalName(calledBody.Parameters[i], calledBody.InvokeID.InvSig.InvokeArgs[i]);

			if(calledBody.InvokeID.InvSig is IRVirtualInvoke)
			{
				IRType thistype; 
				if(calledBody.InvokeID.DefInClass is IRValueType)
					thistype = GlobalInterpreterState.TProg.TypeTable.LookupReferenceTypeFor(calledBody.InvokeID.DefInClass);
				else
					thistype = GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor((IRClassType)calledBody.InvokeID.DefInClass);

				ncf.PopStackArgIntoLocalName(GlobalInterpreterState.TProg.NameTable.LookupLocalVarName("this"), thistype);
			}

			GlobalInterpreterState.PushCallOntoStack(ncf);
		}

		private static void DoAnalyzeOfCall(IRByteCode bc, IRBodyDef b, bool isconsinvoke)
		{
			IRInvokeIdentity caller = GlobalInterpreterState.ActiveCallFrame.MethodIdentity;
			if(!GlobalInterpreterState.TProg.CallGraph.ResolvedCallEdgeAlreadyAdded(caller, b.InvokeID))
				GlobalInterpreterState.TProg.CallGraph.AddAnalysisResolvedCallEdge(caller, b.InvokeID);

			if(b.IsBuiltin)
			{
				TracingSystem.LogBuiltinMethodCallEnter(b.InvokeID);

				GlobalInterpreterState.ActiveCallFrame.SimulateBuiltin(b.BuiltinSemanticName);

				TracingSystem.LogBuiltinMethodCallExit(b.InvokeID);
			}
			else
			{
				TracingSystem.LogMethodCall(b.InvokeID);

				MoveIntoCalleeScope(b, b.Parameters.Count + (b.InvokeID.InvSig is IRVirtualInvoke ? 1 : 0));
				SimulateBody();

				if(isconsinvoke)
				{
					GlobalInterpreterState.ActiveCallFrame.PopInitializedRefBackToStackReturn(b.InvokeID.DefInClass);

					IRType restype = (b.InvokeID.DefInClass is IRClassType) ? GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor((IRClassType)b.InvokeID.DefInClass) : b.InvokeID.DefInClass;
					GlobalInterpreterState.ActiveCallFrame.PushReturnOntoCallerStack(GlobalInterpreterState.CallerCallFrame, restype);
				}
				else
				{
					if(!(b.ReturnType is IRVoidType))
						GlobalInterpreterState.ActiveCallFrame.PushReturnOntoCallerStack(GlobalInterpreterState.CallerCallFrame, b.ReturnType);
				}


				GlobalInterpreterState.ActiveCallFrame.ClearVarsNotInScopeAddNew(new HashSet<IRVarName>(), new Dictionary<IRVarName, IRType>());
				GlobalInterpreterState.PopCallOffStack();

				TracingSystem.LogMethodReturn(b.InvokeID);
			}
		}
	}
}
